package app;

import java.awt.Point;
import java.io.IOException;
import java.net.ServerSocket;
import java.util.Date;
import java.util.Random;
import java.util.Vector;

import app.games.ServerGame;

import util.Constants;

public class Server {
	
	public Server() {
		loginBank = new Vector<SocketConnection>();
		chatBank = new Vector<SocketConnection>();		
		chatThreadBank = new Vector<Thread>();
		games  = new Vector<ServerGame>();
		
		rand = new Random(new Date().getTime());
		
		try {
			loginServer = new ServerSocket(Constants.LOGIN_PORT);
			chatServer = new ServerSocket(Constants.CHAT_PORT);
		}
		catch (IOException e){}
		
		acceptConnectionsService();
		startListener();
		checkBankService();
	}
	
	private long randomID() {
		long r = (rand.nextLong() + MIN_RAND) % MAX_RAND;
		return (r < 0) ? r * -1 : r;
	}
	
	public void acceptConnectionsService() {
		//Accepting connection Servers
		new Thread() {
			public void run(){
				try {
					while (true){
						SocketConnection login = new SocketConnection(loginServer.accept());
						login.send(new Date());
						login.setUser((User)login.receive());
						login.getUser().setId(randomID());
						login.send(login.getUser().getId());
												
						SocketConnection chat = new SocketConnection(chatServer.accept());
						chat.send(new Date());
						
						loginBank.add(login);
						chatBank.add(chat);
						chatThreadBank.add(null);
					}
				}
				catch(Exception e) {e.printStackTrace();}
			}
		}.start();
	}

	public void startListener() {
		//Chat Service
		new Thread() {
			public void run() {
				while (true) {
					try {
						for (int i = 0; i < chatBank.size(); i++) {
							final int ii = i;
							
							Thread t = new Thread() {
								public void run() {
									try {
										int index = 0;
										Request r;
										synchronized(chatBank.get(ii)){
											r = (Request)chatBank.get(ii).receive();
										}
										
										switch (r.getType()) {
											//Chat request
											case Request.CHAT_MSG:
												for (int j = 0; j < chatBank.size(); j++) {
													if (ii != j) {
														chatBank.get(j).send(r);
													}
												}
												break;
												
											//Game request
											case Request.GAME_REQUEST: 
												index = getUserList().indexOf(r.getTarget());
												//setting id game
												r.setMsg(randomID());
												//sending request to target
												chatBank.get(index).send(r);;
												break;
										
											//Game response
											case Request.GAME_RESPONSE:
												index = getUserList().indexOf(r.getSource());
												//sending response to source
												chatBank.get(index).send(r);
												break;
												
											//Player's ready
											case Request.DONE:
												//target user id = game id
												long id = r.getTarget().getId();
												ServerGame s = new ServerGame(id);
												
												int ind = games.indexOf(s);
												if (ind >= 0) {
													s = games.get(ind);
													games.remove(ind);
												}
												
												s.addBoard(r.getMsg(), r.getSource());
												games.add(s);
												
												if (s.getBoardsCount() >= 2) {
													
													index = getUserList().indexOf(s.getUser(1));
													chatBank.get(index).send(r);
													
													r.setType(Request.ATTACK);
													index = getUserList().indexOf(s.getUser(0));
													chatBank.get(index).send(r);
												}
												break;
											
											//asking for position
											case Request.POS_REQUEST:
												id = r.getTarget().getId();
												Point p = (Point)r.getMsg();
												
												s = new ServerGame(id);
												ind = games.indexOf(s);
												s = games.get(ind);
												
												//sending to attacked user
												ind = (s.getUser(0).equals(r.getSource())) ? 1 : 0;
												index = getUserList().indexOf(s.getUser(ind));
												chatBank.get(index).send(r);
												
												//sending to source user
												r.setType(Request.POS_RESPONSE);
												r.setMsg(((boolean[][])s.getBoard(ind))[p.x][p.y]);
												index = getUserList().indexOf(r.getSource());
												chatBank.get(index).send(r);
												break;
										}
									}
									catch(Exception e) {}
								}
							};
							
							if ((chatThreadBank.get(ii) == null) || (!chatThreadBank.get(ii).isAlive())) {
								chatThreadBank.set(ii, t);
								t.start();
							}
							
							sleep(Constants.CONN_CONTROL_TIME);
						}
					}
					catch (Exception e) {e.printStackTrace();}
				}
			}
		}.start();
	}
	
	public void checkBankService() {
		new Thread() {
			public void run() {
				while (true) {
					try {
						for (int i = 0; i < loginBank.size(); i++) {
							Vector<User> uList = getUserList();
							try {
								loginBank.get(i).send(uList);
							}
							catch (IOException e) {
								loginBank.remove(i);
								chatBank.remove(i);
								chatThreadBank.remove(i);
							}
						}
						sleep(Constants.CONN_CONTROL_TIME);
					}
					catch (InterruptedException e) {interrupt();}
				}
			}
		}.start();
	}
	
	public Vector<User> getUserList() {
		Vector<User> v = new Vector<User>();
		
		for (int i = 0; i < loginBank.size(); i++)
				v.add(loginBank.get(i).getUser());
		return v;
	}
	
	public void close() {
		for (int i = 0; i < loginBank.size(); i++) {
			chatThreadBank.get(i).interrupt();
			loginBank.get(i).close();
			chatBank.get(i).close();
		}
	}
	
	private static final long MIN_RAND = 1000000000;
	private static final long MAX_RAND = 2000000000;
	
	private Vector<SocketConnection> loginBank;
	private Vector<SocketConnection> chatBank;
	private Vector<Thread> chatThreadBank;
	private Vector<ServerGame> games;
	
	private ServerSocket loginServer;
	private ServerSocket chatServer;
	
	private Random rand;
}
